#include <stdarg.h>
#include "plc_app.h"
#include "rte_abi.h"
#include "iec_types.h"


const plc_rte_abi_t *plc_rte = 0;

void config_init__(void);

#if defined(PLC_IS_APP) || defined(PLC)

void config_init(void)
{
    config_init__();
}
#endif
#if defined( PLC_IS_APP) || defined(PLC)

extern unsigned long __tick;
#if !defined(windows_x86) && !defined(SYLIXOS)
void setMspAndJump(uint32_t usrAddr)
{
    // Dedicated function with no call to any function (appart the last call)
    // This way, there is no manipulation of the stack here, ensuring that GGC
    // didn't insert any pop from the SP after having set the MSP.
    typedef void (*funcPtr)(void);
    uint32_t jumpAddr = *(volatile uint32_t *)(usrAddr + 0x04); /* reset ptr in vector table */

    funcPtr usrMain = (funcPtr)jumpAddr;

    // SET_REG(SCB_VTOR, (volatile uint32_t)(usrAddr));

    asm volatile("msr msp, %0" ::"g"(*(volatile uint32_t *)usrAddr));

    usrMain(); /* go! */
}
void fake_start(void)
{
    setMspAndJump(0x08000000);
    startPLC(0);
    while (1)
    {
        runPLC();
    };
}


#endif

//Redefine LOG_BUFFER_SIZE
#define LOG_BUFFER_SIZE (1 << 6) /*1Ko*/
#define LOG_BUFFER_ATTRS
extern IEC_TIME __CURRENT_TIME;

void PLC_GetTime(IEC_TIME *CURRENT_TIME)
{
    u64 t = plc_rte->get_time();
    CURRENT_TIME->tv_sec = t / 1000000;
    CURRENT_TIME->tv_nsec = (t % 1000000) * 1000;
}

void PLC_SetTimer(unsigned long long next, unsigned long long period)
{
    plc_rte->set_timer(next, period);
}
void __run(void);
void runPLC(void)
{
    PLC_GetTime(&__CURRENT_TIME);
    __run();
}
int __init();
extern unsigned long long common_ticktime__;
extern char debug_buffer[];
extern long buffer_state;
#define BUFFER_FREE 0
#define BUFFER_BUSY 1
extern unsigned int retain_offset;
void ValidateRetainBuffer(void)
{
#ifndef windows_x86
    plc_rte->validate_retain_buf();
#endif
}
void InValidateRetainBuffer(void)
{
#ifndef windows_x86
    plc_rte->invalidate_retain_buf();
#endif
}
int CheckRetainBuffer(void)
{
#ifndef windows_x86
    return plc_rte->check_retain_buf();
#endif
}

void InitRetain(void)
{
    plc_rte->InitRetain();
}

void CleanupRetain(void)
{
#ifndef windows_x86
    plc_rte->retain_close();
#endif
}

void Retain(unsigned int offset, unsigned int count, void *p)
{
#ifndef windows_x86
    plc_rte->retain(offset, count, p);
#endif
}
void Remind(unsigned int offset, unsigned int count, void *p)
{
#ifndef windows_x86
    plc_rte->remind(offset, count, p);
#endif
}
//@�ָ�������retain������
int RetainCmp(unsigned int offset, unsigned int count, void *p)
{
    // int res = memcmp(retain_info.buf + offset, p, count);
    // memcpy(retain_info.buf + offset, p, count);
}

int startPLC(const void *rte)
{
    plc_rte = (const plc_rte_abi_t *)rte;
    if (__init() == 0)
    {
        PLC_SetTimer(0, common_ticktime__);
        return 0;
    }
    else
    {
        return 1;
    }
}
char pbuf[128];
int rte_log_info(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vsnprintf(pbuf,128,format,args);
    plc_rte->log_info(pbuf);
    va_end(args);
}

int rte_log_debug(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vsnprintf(pbuf,128,format,args);
    plc_rte->log_debug(pbuf);
    va_end(args);
}

int rte_log_warning(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vsnprintf(pbuf,128,format,args);
    plc_rte->log_warning(pbuf);
    va_end(args);
}

int rte_log_error(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vsnprintf(pbuf,128,format,args);
    plc_rte->log_error(pbuf);
    va_end(args);
}
#endif

int LogMessage(u8 level, char *buf, uint32_t size) {
    switch (level) {
        default:
        case LOG_CRITICAL:
            plc_rte->log_error(buf);
            break;
        case LOG_WARNING:
            plc_rte->log_warning(buf);
            break;
        case LOG_DEBUG:
            plc_rte->log_debug(buf);
            break;
        case LOG_INFO:
            plc_rte->log_info(buf);
            break;
    }
    return 0;
}


void delay_app(unsigned int v) {
    plc_rte->delay(v);
}

u8 fast_input(u8 a, u8 b, u8 c, u8 d, u8 e) {
    return plc_rte->input(a, b, c, d, e);
}

void fast_output(u8 a, u8 b, u8 c, u8 d, u8 e, u8 val) {
    plc_rte->output(a, b, c, d, e, val);
}
